﻿using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Flagwind.Data
{
	/// <summary>
	/// 实体类接口。
	/// </summary>
	public interface IEntity : ICloneable, INotifyPropertyChanged
	{
		#region 公共事件

		/// <summary>
		/// 属性获取事件。
		/// </summary>
		event EventHandler<PropertyGettingEventArgs> PropertyGetting;

		#endregion

		#region 索引器

		/// <summary>
		/// 获取或者设置指定属性名称的值，属性名必须是一个实体类的属性（调用了GetProperty 和 SetProperty方法），不能是普通属性。
		/// 如果属性不存在，获取该属性值将为 null，而设置该属性值将抛出异常。
		/// </summary>
		/// <param name="propertyName">属性名称。</param>
		/// <returns>属性值。</returns>
		object this[string propertyName]
		{
			get;
			set;
		}

		/// <summary>
		/// 获取指定索引位置的属性的值。
		/// </summary>
		/// <param name="index">索引位置。</param>
		/// <returns>属性值。</returns>
		object this[int index]
		{
			get;
		}

		#endregion

		#region 字段存取

		/// <summary>
		/// 根据指定的字段名获取属性值。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <param name="fieldType">字段类型。</param>
		/// <returns>属性值。</returns>
		object GetFieldValue(string fieldName, TypeCode fieldType);

		/// <summary>
		/// 根据指定的字段名获取属性值。
		/// </summary>
		/// <typeparam name="T">字段类型。</typeparam>
		/// <param name="fieldName">字段名称。</param>
		/// <returns>属性值。</returns>
		T GetFieldValue<T>(string fieldName);

		/// <summary>
		/// 根据指定的字段名设置属性值。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <param name="value">属性值。</param>
		void SetFieldValue(string fieldName, object value);

		/// <summary>
		/// 根据指定的字段名设置属性值。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <param name="value">属性值。</param>
		/// <param name="size">字段大小。</param>
		void SetFieldValue(string fieldName, string value, int size);

		/// <summary>
		/// 获取设置过的字段名称。
		/// </summary>
		/// <returns>字段名称。</returns>
		string GetChangedFieldName();

		/// <summary>
		/// 获取指定字段的大小。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <returns>字段大小。</returns>
		int GetFieldSize(string fieldName);

		/// <summary>
		/// 设置指定字段的大小。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <param name="fieldSize">字段大小。</param>
		void SetFieldSize(string fieldName, int fieldSize);

		/// <summary>
		/// 获取带架构的表名称，带中括号。
		/// </summary>
		/// <returns>带架构的表名称。</returns>
		string GetSchemeTableName();

		#endregion

		#region 属性存取

		/// <summary>
		/// 获取实体类的属性名/值对。
		/// </summary>
		/// <returns>属性名/值对。</returns>
		EntityEntry GetNameValues();

		/// <summary>
		/// 获取实体类属性改变过的属性名/值对。
		/// </summary>
		/// <returns>属性名/值对。</returns>
		EntityEntry GetChangedNameValues();

		/// <summary>
		/// 设置所有属性的值。
		/// </summary>
		/// <param name="values">数据值数组。</param>
		void SetPropertyValues(object[] values);

		/// <summary>
		/// 新增实体虚拟字段属性，用来传递内容。
		/// </summary>
		/// <param name="name">虚拟字段名称。</param>
		void AddPropertyName(string name);

		/// <summary>
		/// 获取属性的位置索引，如果找不到，返回-1
		/// </summary>
		/// <param name="propertyName">属性名。</param>
		/// <returns>属性的位置索引，如果找不到，返回-1。</returns>
		int GetPropertyIndex(string propertyName);

		/// <summary>
		/// 获取属性列的值。
		/// </summary>
		/// <param name="propertyName">属性名称。</param>
		/// <returns>属性值。</returns>
		object GetPropertyValue(string propertyName);

		/// <summary>
		/// 获取属性的值，如果没有，将返回属性类型的默认值。
		/// 注意该方法调用将发起属性获取事件。
		/// </summary>
		/// <param name="propertyName">属性名称。</param>
		/// <returns>属性值。</returns>
		object GetPropertyValueWithEvent(string propertyName);

		#endregion

		#region 属性状态改变

		/// <summary>
		/// 重置实体类全部属性的修改标记。
		/// <remarks>注意，DataAccess 将根据该标记决定更新哪些字段到数据库，它只更新标记为已经修改的实体类属性。</remarks>
		/// </summary>
		/// <param name="flag">是否已经修改。</param>
		void ResetChanges(bool flag = false);

		/// <summary>
		/// 设置属性的值是否跟默认值一样，如果一样则表示该属性未更改过。
		/// </summary>
		void SetDefaultChanges();

		#endregion

		#region 实体映射

		/// <summary>
		/// 将实体类的表名称映射到一个新的表名称。
		/// </summary>
		/// <param name="name">新的表名称。</param>
		/// <returns>是否成功。</returns>
		bool ChangeMappingTable(string name);

		/// <summary>
		/// 获取对应的父实体类的外键字段名称，如果没有，返回空字符串。
		/// </summary>
		/// <typeparam name="Parent">父实体类。</typeparam>
		/// <returns>外键字段名称，必须是当前实体类使用的一个字段。</returns>
		string GetForeignKey<Parent>() where Parent : IEntity, new();

		/// <summary>
		/// 从POCO实体类获取跟当前实体类的属性名称相同的属性的值，拷贝到当前实体类中，完成数据的映射。
		/// 要求拷贝的同名属性是读写属性且类型相同。
		/// </summary>
		/// <param name="pocoClass">POCO实体类，提供源数据。</param>
		/// <param name="isChange">是否改变属性的修改状态。</param>
		/// <returns>映射成功的属性数量。</returns>
		int MapFrom(object pocoClass, bool isChange = false);

		/// <summary>
		/// 将当前实体类的属性值（非空值）映射到相同属性名称的POCO实体类中。要求拷贝的同名属性是读写属性且类型相同。
		/// </summary>
		/// <param name="pocoClass">POCO实体类。</param>
		/// <returns>映射成功的属性数量。</returns>
		int MapTo(object pocoClass);

		#endregion

		#region 其他方法

		/// <summary>
		/// 测试写入属性。
		/// </summary>
		void TestWriteProperty();

		#endregion

		#region 映射方法

		/// <summary>
		/// 实体类的映射类型。
		/// </summary>
		MappingType GetMappingType();

		/// <summary>
		/// 实体类对应的映射名称。
		/// </summary>
		string GetMappingName();

		/// <summary>
		/// 实体类映射的表的架构名字。
		/// </summary>
		string GetScheme();

		/// <summary>
		/// 标识字段名称（有些数据库可能内置不支持），该字段不可更新，但是插入数据的时候可以获取该字段。
		/// </summary>
		string GetIdentity();

		/// <summary>
		/// 获取主键字段名称列表。
		/// </summary>
		List<string> GetPrimaryKey();

		/// <summary>
		/// 获取属性名数组。
		/// </summary>
		string[] GetPropertyNames();

		void SetPropertyNames(string[] names);

		/// <summary>
		/// 获取属性值数组。
		/// </summary>
		object[] GetPropertyValues();

//		void SetPropertyValues(object[] values);

		/// <summary>
		/// 属性值被改变的属性名列表。
		/// </summary>
		List<string> GetPropertyChangedList();

		#endregion
	}
}